﻿#pragma once

#include <map>
#include <condition_variable>
#include <thread>
#include <functional>

class Timer
{
public:
	typedef std::function<void(int id, size_t data)> TimerCallback;

private:
	struct TimerTask
	{
		bool repeated;
		int id;
		int delay;
		std::chrono::milliseconds time;
		size_t data;
		TimerCallback action;
	};

private:
	bool m_running;
	int m_seed;
	std::thread m_thread;
	std::condition_variable m_cv;
	std::mutex m_mtx;
	std::map<int, TimerTask> m_tasks;

private:
	inline std::chrono::milliseconds now()
	{
		return std::chrono::duration_cast<std::chrono::milliseconds>(
			std::chrono::steady_clock::now().time_since_epoch());
	}

	void proceduce();

public:
	Timer()
		: m_running(true)
		, m_seed(1)
		, m_thread(&Timer::proceduce, this)
	{
	}

	~Timer();

	// @brief Schedules the task for execution after delay time.
	// @param action: the action of task.
	// @param delay: delay time, in milliseconds.
	// @param data: the data of action
	// @param repeated: true execute the task repeated, or false for once.
	int schedule(TimerCallback &&action,
		int delay, size_t data = 0, bool repeated = false);

	// @brief Cancel the task.
	// @param id: task id
	// @return true for success and false if the task is not exist.
	bool cancel(int id);

private:
	Timer(const Timer&) = delete;
	Timer(Timer&&) = delete;
	Timer &operator=(const Timer&) = delete;
	Timer &operator=(Timer&&) = delete;
};

